using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using Stunt.Nutss;

namespace Stunt.Nutss.Stunt
{

	/// <summary>Returns the observed address and port to STUNT clients.</summary>
	public class STUNTServer
	{

		#region Variables

		/// <summary>
		/// TCP passive socket that clients connect to
		/// </summary>
		private Socket server = null;

		#endregion

		#region Constructor

		/// <summary>
		/// Creates a new Responder thread.
		/// </summary>
		/// <param name="address">Local address to bind to.</param>
		/// <param name="port">Local port to bind to. 0 for random.</param>
		public STUNTServer(IPAddress address, int port)
		{
			server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			server.Bind(new IPEndPoint(address, port));
			server.Listen(5);
		}

		#endregion

		#region GetSocketAddress

		/// <summary>
		/// Returns the local IP and port the server is listening on.
		/// </summary>
		/// <returns>Server socket address</returns>
		public EndPoint GetSocketAddress()
		{
			return server.LocalEndPoint;
		}

		#endregion

		#region Inner class: TCPThread

		private class TCPThread
		{
			private Socket _socket;

			public TCPThread(Socket socket)
			{
				_socket = socket;
			}

			/// <summary>
			/// Starts the responder thread that responds to TCP clients in a tight loop.
			/// </summary>
			public void ThreadProcess()
			{

				try
				{

					while (true)
					{
						Socket clientSocket = _socket.Accept();

						try
						{
							BinaryFormatter formatter = new BinaryFormatter();
							MemoryStream stream = new MemoryStream();

							formatter.Serialize(stream, clientSocket.RemoteEndPoint);
							clientSocket.Send(stream.GetBuffer(), (int)stream.Length, SocketFlags.None);
							clientSocket.Close();
						}
						catch (Exception e)
						{
							Debug.WriteLine("TCPThread - ThreadProcess" + e.Message);
							break;
						}
						finally
						{

							try
							{
								clientSocket.Close();
							}
							catch (Exception) { }
						}
					}
				}
				catch (Exception e)
				{
					Debug.WriteLine("TCPThread - ThreadProcess" + e.Message);
				}
				finally
				{

					try
					{
						_socket.Close();
					}
					catch (Exception) { }
				}
			}
		}

		#endregion

		#region Inner class: UDPThread NOT USED Vestigial Code

		//private class UDPThread {
		//    private Socket _socket;
		//    public UDPThread(Socket socket) {
		//        _socket = socket;
		//    }
		//    /// <summary>
		//    /// Starts the responder thread that responds to TCP clients in a tight loop.
		//    /// </summary>
		//    public void ThreadProcess() {
		//        byte[] buf = new byte[1500];
		//        try {
		//            while (true) {
		//                EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
		//                _socket.ReceiveFrom(buf, ref endPoint);
		//                try {
		//                    MemoryStream stream = new MemoryStream();
		//                    BinaryFormatter formatter = new BinaryFormatter();
		//                    formatter.Serialize(stream, endPoint);
		//                    _socket.SendTo(stream.GetBuffer(), (int)stream.Length, SocketFlags.None, endPoint);
		//                } catch (Exception e) {
		//                    Debug.WriteLine("UDPThread - ThreadProcess" + e.Message);
		//                    try {
		//                        _socket.Close();
		//                    } catch (Exception) { }
		//                    break;
		//                }
		//            }
		//        } catch (Exception e) {
		//            Debug.WriteLine("UDPThread - ThreadProcess" + e.Message);
		//        } finally {
		//            try {
		//                _socket.Close();
		//            } catch (Exception) { }
		//        }
		//    }
		//}
		#endregion

		#region ThreadProcess

		public void ThreadProcess()
		{
			new Thread(new ThreadStart(new TCPThread(this.server).ThreadProcess)).Start();
			//new Thread(new ThreadStart(new UDPThread(this.server2).ThreadProcess)).Start();
		}

		#endregion

		#region Main

		/// <summary>
		/// Starts a standalone server
		/// </summary>
		/// <param name="args">Program arguments</param>
		public static void Main(string[] args)
		{

			if (args.Length != 2)
			{
				Console.WriteLine("Usage:\n\tSTUNTServer ip port\n");
				Environment.Exit(1);
			}

			try
			{
				STUNTServer r = new STUNTServer(IPAddress.Parse(args[0]), Int32.Parse(args[1]));
				Debug.WriteLine("STUNT Server: {0}", r.GetSocketAddress().ToString());
				new Thread(new ThreadStart(r.ThreadProcess)).Start();
			}
			catch (Exception e)
			{
				Debug.WriteLine(e.StackTrace);
			}
		}

		#endregion

	}
}